﻿using System;
using System.Windows;
using System.Windows.Media;

namespace CodeAngel.UI.Extensions
{
    public static class FindExtensions
    {

        /// <summary>
        /// Finds the first ancestor of a given type.
        /// </summary>
        /// <typeparam name="T">The type to search for</typeparam>
        /// <param name="child">The child.</param>
        /// <returns>The ancestor or null if not found</returns>
        public static T FindFirstAncestorOfType<T>(this DependencyObject child)
        {
            DependencyObject parent = VisualTreeHelper.GetParent(child);
            if (parent != null && child != null)
            {
                if (parent is T)
                {
                    return (T)((object)parent);
                }
                else
                {
                    return FindFirstAncestorOfType<T>(parent);
                }
            }
            else
            {
                return default(T);
            }
        }

        /// <summary>
        /// Finds the child by data context.
        /// </summary>
        /// <typeparam name="T">The type of the child to be matched</typeparam>
        /// <param name="depObj">The dep obj.</param>
        /// <param name="context">The context.</param>
        /// <returns>The child of the specified type with matching DataContext or null</returns>
        public static T FindChildByDataContext<T>(this DependencyObject depObj, object context) where T : DependencyObject
        {
            if (depObj != null)
            {
                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
                {
                    DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
                    if (child != null && child is T)
                    {
                        FrameworkElement ele = child as FrameworkElement;
                        if (ele != null && context.Equals(ele.DataContext))
                        {
                            return (T)child;
                        }
                    }

                    T childItem = FindChildByDataContext<T>(child, context);
                    if (childItem != null)
                    {
                        return childItem;
                    }
                }
            }

            return null;
        }

        /// <summary>
        /// Find the child of the type
        /// </summary>
        /// <typeparam name="T">type of child to find</typeparam>
        /// <param name="depObj">start of search</param>
        /// <returns>child or null if not found</returns>
        public static T FindVisualChild<T>(this DependencyObject depObj) where T : DependencyObject
        {
            // only works well if child is of a unique type, otherwise returns the first child of that type.
            if (depObj != null)
            {
                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
                {
                    DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
                    if (child != null && child is T)
                    {
                        return (T)child;
                    }

                    T childItem = FindVisualChild<T>(child);
                    if (childItem != null)
                    {
                        return childItem;
                    }
                }
            }

            return null;
        }

        /// <summary>
        /// Find a child of given type
        /// </summary>
        /// <typeparam name="T">type of child to find</typeparam>
        /// <param name="depObj">where to start looking for child</param>
        /// <param name="dataContext">datacontext of child equals this</param>
        /// <returns>found child or null</returns>
        public static T FindVisualChild<T>(this DependencyObject depObj, Object dataContext) where T : DependencyObject
        {
            if (depObj != null)
            {
                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
                {
                    DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
                    if (child != null && child is T)
                    {
                        FrameworkElement ele = child as FrameworkElement;
                        if (ele != null && dataContext.Equals(ele.DataContext))
                        {
                            return (T)child;
                        }
                    }

                    T childItem = FindVisualChild<T>(child, dataContext);
                    if (childItem != null)
                    {
                        return childItem;
                    }
                }
            }

            return null;
        }

        /// <summary>
        /// Find a parent of given type
        /// </summary>
        /// <typeparam name="T">type of child to find</typeparam>
        /// <param name="depObj">where to start looking for child</param>
        /// <returns>found parent or null</returns>
        public static T FindVisualParent<T>(this DependencyObject depObj) where T : DependencyObject
        {
            if (depObj != null)
            {
                DependencyObject parent = VisualTreeHelper.GetParent(depObj);
                if (parent != null && parent is T)
                {
                    FrameworkElement ele = parent as FrameworkElement;
                    if (ele != null)
                    {
                        return (T)parent;
                    }
                }

                T parentItem = FindVisualParent<T>(parent);
                if (parentItem != null)
                {
                    return parentItem;
                }
            }

            return null;
        }

        /// <summary>
        /// Finds the logical ancestor.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="depObj">The dep obj.</param>
        /// <returns></returns>
        public static T FindLogicalAncestor<T>(this DependencyObject depObj) where T : DependencyObject
        {
            if (depObj != null)
            {
                DependencyObject parent = LogicalTreeHelper.GetParent(depObj);
                if (parent != null && parent is T)
                {
                    FrameworkElement ele = parent as FrameworkElement;
                    if (ele != null)
                    {
                        return (T)parent;
                    }
                }

                T parentItem = FindLogicalAncestor<T>(parent);
                if (parentItem != null)
                {
                    return parentItem;
                }
            }

            return null;
        }

        /// <summary>
        /// Determines whether [contains] [the specified haystack].
        /// </summary>
        /// <param name="haystack">The haystack.</param>
        /// <param name="needle">The needle.</param>
        /// <returns>
        /// 	<c>true</c> if [contains] [the specified haystack]; otherwise, <c>false</c>.
        /// </returns>
        public static bool Contains(this DependencyObject haystack, DependencyObject needle)
        {
            bool contains = haystack == needle;
            if (!contains)
            {
                int childCount = VisualTreeHelper.GetChildrenCount(haystack);
                for (int i = 0; i < childCount; i++)
                {
                    DependencyObject newHaystack = VisualTreeHelper.GetChild(haystack, i);
                    contains = Contains(newHaystack, needle);
                    if (contains) break;
                }
            }
            return contains;
        }

    }
}
